package se.webbzon.oschi01.tools.converter;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.Observer;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import se.webbzon.oschi01.gui.MainFrame;
import se.webbzon.oschi01.gui.MainModel;
import se.webbzon.oschi01.gui.MinecraftIconSheet;
import se.webbzon.oschi01.item.ItemEntry;
import se.webbzon.oschi01.item.TranslationJsonReader;
import se.webbzon.oschi01.singleton.Singletons;
import se.webbzon.oschi01.utils.Progress;
import se.webbzon.oschi01.utils.ProgressEvent;
import se.webbzon.oschi01.world.FileWorld;
import se.webbzon.oschi01.world.World;
import se.webbzon.oschi01.world.WorldFileWriter;
import se.webbzon.oschi01.world.WorldWriter;

public class ConverterFrame extends JDialog {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2315939951863862899L;

	Converter converter = new Converter();

	JProgressBar progressBar;
	
	JLabel progressLabel;

	JButton destinationButton;

	JCheckBox copyCheckBox, resolveCheckBox;

	JTextField translationField, blockField, itemField, destinationField;

	public ConverterFrame() {
		super(Singletons.instance(MainFrame.class));

		setIconImage(Singletons.instance(MinecraftIconSheet.class).getIcon(new Point(2,6)).getImage());
		setTitle("Converter Tool");
		getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));

		JPanel filePanel = new JPanel();
		filePanel.setLayout(new GridLayout(2,2));
		filePanel.add(new JLabel("Object translation file:"));
		filePanel.add(Box.createGlue());
		translationField = new JTextField("<no file selected>");
		translationField.setEditable(false);
		filePanel.add(translationField);
		filePanel.add(new JButton(new TranslationAction()));
		add(filePanel);

		add(Box.createVerticalStrut(8));

		JPanel optionPanel = new JPanel();
		optionPanel.setLayout(new BoxLayout(optionPanel,BoxLayout.X_AXIS));
		copyCheckBox = new JCheckBox(new CopyAction());
		copyCheckBox.setSelected(true);
		optionPanel.add(copyCheckBox);
		resolveCheckBox = new JCheckBox("Resolve item structures");
		resolveCheckBox.setSelected(true);
		optionPanel.add(resolveCheckBox);
		optionPanel.add(Box.createHorizontalStrut(4));
		blockField = new JTextField("0");
		blockField.getDocument().addDocumentListener(new Listener());
		blockField.setPreferredSize(new Dimension(48,24));
		optionPanel.add(blockField);
		optionPanel.add(new JLabel("Block fallback ID"));
		optionPanel.add(Box.createHorizontalStrut(4));
		itemField = new JTextField("1");
		itemField.getDocument().addDocumentListener(new Listener());
		itemField.setPreferredSize(new Dimension(48,24));
		optionPanel.add(itemField);
		optionPanel.add(new JLabel("Item fallback ID"));
		add(optionPanel);

		JPanel destinationPanel = new JPanel();
		destinationPanel.setLayout(new GridLayout(2,2));
		destinationPanel.add(new JLabel("Destination directory:"));
		destinationPanel.add(Box.createGlue());
		destinationField = new JTextField("<no file selected>");
		destinationField.setEditable(false);
		destinationPanel.add(destinationField);
		destinationButton = new JButton(new DestinationAction());
		destinationPanel.add(destinationButton);
		add(destinationPanel);

		add(Box.createVerticalStrut(8));

		JPanel progressPanel = new JPanel();
		progressPanel.setLayout(new BoxLayout(progressPanel,BoxLayout.Y_AXIS));
		progressLabel = new JLabel("Idle");
		progressPanel.add(progressLabel);
		progressBar = new JProgressBar();
		progressPanel.add(progressBar);
		add(progressPanel);

		add(Box.createVerticalStrut(8));

		JPanel convertPanel = new JPanel();
		convertPanel.setLayout(new BoxLayout(convertPanel,BoxLayout.X_AXIS));
		//copyCheckBox = new JCheckBox("");
		//convertPanel.add(new )
		convertPanel.add(new JButton(new ConvertAction()));
		add(convertPanel);

		pack();
		setResizable(false);
		setVisible(true);
	}

	private final class CopyAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1668013989608958596L;

		public CopyAction() {
			putValue(Action.NAME,"Convert copy");
		}

		@Override public void actionPerformed(ActionEvent arg0) {
			destinationButton.setEnabled(copyCheckBox.isSelected());
		}

	}

	private final class TranslationAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = -6105444442669271498L;

		public TranslationAction() {
			putValue(Action.NAME,"Browse...");
		}

		@Override public void actionPerformed(ActionEvent arg0) {
			JFileChooser fc = new JFileChooser();
			int val = fc.showOpenDialog(ConverterFrame.this);
			if (val == JFileChooser.APPROVE_OPTION) {
				File file = fc.getSelectedFile();
				if (file.exists())
					translationField.setText(file.getAbsolutePath());
			}
		}

	}

	private final class DestinationAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = 7228083668453838039L;

		public DestinationAction() {
			putValue(Action.NAME,"Browse...");
		}

		@Override public void actionPerformed(ActionEvent arg0) {
			JFileChooser fc = new JFileChooser();
			fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			int val = fc.showSaveDialog(ConverterFrame.this);
			if (val == JFileChooser.APPROVE_OPTION) {
				File file = fc.getSelectedFile();
				destinationField.setText(file.getAbsolutePath());
			}
		}

	}

	private final class ConvertAction extends AbstractAction {

		/**
		 * 
		 */
		private static final long serialVersionUID = -2047312262494533051L;

		public ConvertAction() {
			putValue(Action.NAME,"Convert");
		}

		@Override public void actionPerformed(ActionEvent e) {
			boolean copy = copyCheckBox.isSelected();
			ConverterFrame.this.setEnabled(false);
			converter.setResolveItemStructures(resolveCheckBox.isSelected());
			try {
				if (copy)
					copyAndTranslate();
				else
					translate(0,1);
			} catch (IOException ex) {
				JOptionPane.showMessageDialog(ConverterFrame.this, ex.getMessage());
				ConverterFrame.this.setEnabled(true);
			}
		}

		private void copyAndTranslate() throws IOException {
			final MainModel model = Singletons.instance(MainFrame.class).getModel();
			final File destination = new File(destinationField.getText());
			WorldFileWriter writer = new WorldFileWriter();
			writer.getProgress().addObserver(new Observer() {

				@Override public void update(Observable o, Object arg) {
					Progress<?> p = ((ProgressEvent) arg).getSource();
					progressLabel.setText(p.getText());
					if (p.isRunning()) {
						progressBar.setValue(p.getProgress()/2);
					} else {
						progressBar.setValue(0);
						o.deleteObserver(this);
						if (p.isAborted())
							ConverterFrame.this.setEnabled(true);
						else try {
							model.setWorld(null);
							FileWorld world = new FileWorld(destination);
							model.setWorld(world);
							translate(50,2);
						} catch (IOException e) {
							JOptionPane.showMessageDialog(ConverterFrame.this, "Conversion aborted due to exception!\n" + e.getMessage()); 
						}
					}
				}
			});

			WorldWriter.write(writer, model.getWorld(), destination);
		}

		private void translate(final int start, final int div) throws IOException {
			World world = Singletons.instance(MainFrame.class).getModel().getWorld();
			File translationFile = new File(translationField.getText());
			FileInputStream fis = new FileInputStream(translationFile);
			TranslationJsonReader reader = new TranslationJsonReader();
			Map<ItemEntry,ItemEntry> translation = reader.read(fis);
			converter.clearMapping();
			for (Entry<ItemEntry,ItemEntry> entry : translation.entrySet()) {
				converter.addMapping(entry.getKey().getId(), entry.getValue().getId());
			}
			// Convert world
			converter.convert(world).addObserver(new Observer() {

				@Override public void update(Observable o, Object arg) {
					Progress<?> p = ((ProgressEvent) arg).getSource();
					progressLabel.setText(p.getText());
					if (p.isRunning()) {
						progressBar.setValue(start + p.getProgress()/div);
					} else {
						progressBar.setValue(0);
						o.deleteObserver(this);
						ConverterFrame.this.setEnabled(true);
						if (p.isAborted()) {
							Exception e = p.getException();
							e.printStackTrace();
							JOptionPane.showMessageDialog(ConverterFrame.this, "Conversion aborted due to exception!\n" + e.getMessage()); 
						} else
							JOptionPane.showMessageDialog(ConverterFrame.this, "Conversion complete!");
					}
				}

			});
		}
	}

		private final class Listener implements DocumentListener {

			@Override public void changedUpdate(DocumentEvent arg0) {
				update();
			}

			@Override public void insertUpdate(DocumentEvent arg0) {
				update();
			}

			@Override public void removeUpdate(DocumentEvent arg0) {
				update();
			}

			private final void update() {
				try {
					int blockFallbackId = Integer.parseInt(blockField.getText());
					if (converter.setFallbackBlockId(blockFallbackId))
						blockField.setBackground(Color.white);
					else
						blockField.setBackground(Color.red);
				} catch (NumberFormatException e) {
					blockField.setBackground(Color.red);
				}
				try {
					int itemFallbackId = Integer.parseInt(itemField.getText());
					if (converter.setFallbackItemId(itemFallbackId))
						itemField.setBackground(Color.white);
					else
						itemField.setBackground(Color.red);
				} catch (NumberFormatException e) {
					itemField.setBackground(Color.red);
				}
			}

		}

	}
